package cn.xie.domain.strategy.service.armory;

import cn.xie.domain.strategy.model.entity.StrategyAwardEntity;
import cn.xie.domain.strategy.model.entity.StrategyEntity;
import cn.xie.domain.strategy.model.entity.StrategyRuleEntity;
import cn.xie.domain.strategy.repository.IStrategyRepository;
import cn.xie.types.common.Constants;
import cn.xie.types.enums.ResponseCode;
import cn.xie.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: xiaoxie
 * create: 2025-08-04 14:05
 * @BelongsProject: bit-market
 * @BelongsPackage: cn.xie.domain.strategy.service.armory
 * description: 策略装配库(兵工厂)，负责初始化策略计算
 */
@Slf4j
@Service
public class StrategyArmoryDispatch implements IStrategyArmory, IStrategyDispatch{

    @Resource
    private IStrategyRepository strategyRepository;
    /**
     * @description: 装配抽奖策略配置「触发的时机可以为活动审核通过后进行调用」
     * @author: xiaoxie
     * @date: 2025/8/4 下午2:06
     * @param: [strategyId]
     * @return: boolean
     **/

    /** 固定放大倍数，避免极端小数导致内存爆炸 */
    private static final int SCALE = 10_000;

    @Override
    public boolean assembleLotteryStrategy(Long strategyId) {
        // 1. 查询策略奖品
        List<StrategyAwardEntity> entities = strategyRepository.queryStrategyAwardList(strategyId);
        if (entities == null || entities.isEmpty()) {
            return false;
        }
        // 无论如何都要先生成好table
        assembleLotteryStrategyRate(String.valueOf(strategyId),entities);
        // 先查询策略表看是否需要装配权重
        StrategyEntity strategyEntity = strategyRepository.queryStrategyByStrategyId(strategyId);
        if(null == strategyEntity.getRuleModelWeight()) {
            return true;
        }
        // 如果需要的话那么就需要查询具体的规则是什么样的 - 获取策略规则实体
        StrategyRuleEntity strategyRuleEntity = strategyRepository.queryStrategyRuleByStrategyId(strategyId, strategyEntity.getRuleModelWeight());
        if(null == strategyRuleEntity) {
            throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode()
                    ,ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
        }
        // 获取rule_value_map
        Map<String, List<Integer>> ruleValueMap = strategyRuleEntity.getRuleValueMap();
        // 克隆对象
        List<StrategyAwardEntity> strategyAwardListClone = new ArrayList<>(entities);
        //根据不同的情况获得不同抽奖情况
        Set<String> ruleValueMapKey = ruleValueMap.keySet();
        for(String key : ruleValueMapKey) {
            String rateKey = String.valueOf(strategyId).concat(Constants.UNDERSCORE).concat(key);
            // 取出当前 key 对应的奖品 ID 集合
            Set<Integer> allowIds = new HashSet<>(ruleValueMap.get(key));
            //排除除了map列表的奖品id的数
            List<StrategyAwardEntity> filtered = strategyAwardListClone.stream()
                    .filter(e -> allowIds.contains(e.getAwardId()))
                    .collect(Collectors.toList());
            assembleLotteryStrategyRate(rateKey, filtered);
        }
        return true;
    }
    /**
     * @description: 生成策略抽奖的table
     * @author: xiaoxie
     * @date: 2025/8/5 下午10:02
     * @param: [key, entities]
     * @return: void
     **/
    private void assembleLotteryStrategyRate(String key, List<StrategyAwardEntity> entities) {
        // 1. 过滤 0 概率 & 计算总概率
        entities.removeIf(e -> e.getAwardRate().compareTo(BigDecimal.ZERO) <= 0);
        BigDecimal totalRate = entities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 2. 生成概率查找表（固定 10 000 份）
        List<Integer> rateTable = new ArrayList<>();
        for (StrategyAwardEntity e : entities) {
            int copies = e.getAwardRate()
                    .multiply(BigDecimal.valueOf(SCALE))
                    .setScale(0, RoundingMode.HALF_UP)
                    .intValue();
            for (int i = 0; i < copies; i++) {
                rateTable.add(e.getAwardId());
            }
        }

        // 3. 打乱顺序并转成 Map
        Collections.shuffle(rateTable);
        Map<Integer, Integer> shuffleMap = new LinkedHashMap<>();
        for (int i = 0; i < rateTable.size(); i++) {
            shuffleMap.put(i, rateTable.get(i));
        }

        // 4. 存入 Redis
        strategyRepository.storeStrategyAwardSearchRateTable(
                key,
                shuffleMap.size(),
                shuffleMap
        );

    }

    @Override
    public Integer getRandomAwardId(Long strategyId) {
        // 获取到频率范围
        int rateRange = strategyRepository.getRateRange(String.valueOf(strategyId));
        return strategyRepository.getStrategyAwardAssemble(String.valueOf(strategyId),new Random().nextInt(rateRange));
    }

    @Override
    public Integer getRandomAwardId(Long strategyId, String ruleModel) {
        String rateKey = String.valueOf(strategyId).concat(Constants.UNDERSCORE).concat(ruleModel);
        int rateRange = strategyRepository.getRateRange(rateKey);
        return strategyRepository.getStrategyAwardAssemble(rateKey,new Random().nextInt(rateRange));
    }


}
